Tablo = function(props) {
	var tablo = this, i, row, col, pektis, noumero;

	this.tetragono = {};
	this.pagos = {};
	this.pioni = {}
	this.fasiSet('ΚΑΤΑΣΚΕΥΗ');

	for (row = 1; row <= 6; row++) {
		for (col = 1; col <= 6; col++) {
			this.tetragono[geister.rc(row, col)] = new Tetragono({
				tablo: tablo,
				row: row,
				col: col,
			});
		}
	}

	for (pektis = 1; pektis <= geister.thesiMax; pektis++) {
		this.pagos[pektis] = new Pagos();
		for (noumero = 1; noumero <= 4; noumero++) {
			this.pioni[geister.pn(pektis, noumero)] = new Pioni({
				tablo: tablo,
				pektis: pektis,
				noumero: noumero,
			}).valePago();
			this.pioni[geister.pn(pektis, noumero + 4)] = new Pioni({
				tablo: tablo,
				pektis: pektis,
				noumero: noumero + 4,
			}).valePago();
		}
	}

	globals.initObject(this, props, {
		recursive: false,
	});
}

Tablo.prototype.skinikoGet = function() {
	return this.skiniko;
}

Tablo.prototype.skinikoFetch = function() {
	if (this.hasOwnProperty('skiniko')) return this.skiniko;
	globals.fatal('απροσδιόριστο σκηνικό ταμπλό');
}

Tablo.prototype.trapeziGet = function() {
	return this.trapezi;
}

Tablo.prototype.trapeziFetch = function() {
	var trapezi = this.trapeziGet();
	if (trapezi) return trapezi;
	globals.fatal('απροσδιόριστο τραπέζι ταμπλό');
}

Tablo.prototype.fasiGet = function() {
	return this.fasi;
}

Tablo.prototype.fasiFetch = function() {
	var fasi = this.fasiGet();
	if (fasi) return fasi;
	globals.fatal('απροσδιόριστη φάση ταμπλό');
}

Tablo.prototype.fasiSet = function(fasi) {
	this.fasi = fasi;
	return this;
}

Tablo.prototype.isFasi = function() {
	var i, fasi = this.fasiGet();
	for (i = 0; i < arguments.length; i++) {
		if (fasi == arguments[i]) return true;
	}
	return false;
}

Tablo.prototype.oxiFasi = function() {
	var i, fasi = this.fasiGet();
	for (i = 0; i < arguments.length; i++) {
		if (fasi == arguments[i]) return false;
	}
	return true;
}

Tablo.prototype.epomenosGet = function() {
	return this.epomenos;
}

Tablo.prototype.epomenosSet = function(pektis) {
	if (pektis === undefined) {
		this.epomenos++;
		if (this.epomenos > geister.thesiMax) this.epomenos = 1;
	}
	else this.epomenos = pektis;
	return this;
}

Tablo.prototype.epomenosClear = function() {
	delete this.epomenos;
	return this;
}

Tablo.prototype.tetragonoGet = function() {
	var rc;

	switch (arguments.length) {
	case 1:
		rc = arguments[0];
		break;
	case 2:
		rc = geister.rc(arguments[0], arguments[1]);
		break;
	default:
		globals.fatal('invalid "tetragono" index');
	}

	return this.tetragono[rc];
}

Tablo.prototype.pagosGet = function(pektis) {
	return this.pagos[pektis];
}

Tablo.prototype.pioniGet = function() {
	var pn;

	switch (arguments.length) {
	case 1:
		pn = arguments[0];
		break;
	case 2:
		pn = geister.pn(arguments[0], arguments[1]);
		break;
	default:
		globals.fatal('invalid "pioni" index');
	}

	return this.pioni[pn];
}

Tablo.prototype.tetragonoWalk = function(callback) {
	var row, col;

	for (row = 1; row <= 6; row++) {
		for (col = 1; col <= 6; col++) {
			callback(geister.rc(row, col), this.tetragonoGet(row, col));
		}
	}

	return this;
}

Tablo.prototype.pagosWalk = function(callback) {
	var pektis;

	for (pektis = 1; pektis <= geister.thesiMax; pektis++) {
		callback(this.pagosGet(pektis));
	}

	return this;
}

Tablo.prototype.pioniWalk = function(callback) {
	var pektis, noumero;

	for (pektis = 1; pektis <= geister.thesiMax; pektis++) {
		for (noumero = 1; noumero <= 8; noumero++) {
			callback(this.pioni[geister.pn(pektis, noumero)]);
		}
	}

	return this;
}

Tablo.prototype.nikitisGet = function() {
	return this.nikitis;
}

Tablo.prototype.nikitisSet = function(pektis) {
	var trapezi = this.trapeziGet();

	if (trapezi) trapezi.apodoxiSet('ΟΧΙ');
	this.nikitis = pektis;
	this.epomenosClear();
	this.fasiSet('ΤΕΛΟΣ');
	return this;
}

// Η μέθοδος που ακολουθεί τοποθετεί τα πιόνια των παικτών στις αρχικές τους θέσεις επάνω
// στο ταμπλό. Τα «καλά» φαντασματάκια τοποθετούνται στις εξωτερικές γραμμές τού ταμπλό,
// στις στήλες 2 έως 5, ενώ τα «κακά» φαντασματάκια στις πιο μέσα γραμμές. Εκκενώνουμε,
// επίσης, τους «πάγκους» στους οποίους εναποθέτουμε τα «φαγωμένα» φαντασματάκια.

Tablo.prototype.stisimo = function() {
	var pektis, noumero, col;

	// Εκκενώνουμε τα τετράγωνα τού ταμπλό αποσυσχετίζοντάς τα από οποιοδήποτε
	// φαντασματάκι.

	this.tetragonoWalk(function(rc, tetragono) {
		delete tetragono.pioni;
	});

	// Εκκενώνουμε τους δύο «πάγκους».

	this.pagosWalk(function(pagos) {
		pagos.pioni = [];
	});

	// Διαγράφουμε τα στοιχεία θέσης για όλα τα φαντασματάκια, επομένως για το
	// ελάχιστο διάστημα που ακολουθεί αυτά βρίσκονται «στον αέρα».

	this.pioniWalk(function(pioni) {
		delete pioni.pagos;
		delete pioni.tetragono;
	});

	// Τοποθετούμε τα «καλά» φαντασματάκια τού πρώτου παίκτη στις στήλες 2 έως 5
	// της πρώτης γραμμής τού ταμπλό.

	for (col = 2, noumero = 1; noumero <= 4; noumero++, col++) {
		this.pioniGet(1, noumero).valeTetragono(this.tetragonoGet(1, col));
	}

	// Τοποθετούμε τα «κακά» φαντασματάκια τού πρώτου παίκτη στις στήλες 2 έως 5
	// της δεύτερης γραμμής τού ταμπλό.

	for (col = 2, noumero = 5; noumero <= 8; noumero++, col++) {
		this.pioniGet(1, noumero).valeTetragono(this.tetragonoGet(2, col));
	}

	// Τοποθετούμε τα «καλά» φαντασματάκια τού δεύτερου παίκτη στις στήλες 2 έως 5
	// της έκτης γραμμής τού ταμπλό.

	for (col = 2, noumero = 1; noumero <= 4; noumero++, col++) {
		this.pioniGet(2, noumero).valeTetragono(this.tetragonoGet(6, col));
	}

	// Τοποθετούμε τα «κακά» φαντασματάκια τού δεύτερου παίκτη στις στήλες 2 έως 5
	// της πέμπτης γραμμής τού ταμπλό.

	for (col = 2, noumero = 5; noumero <= 8; noumero++, col++) {
		this.pioniGet(2, noumero).valeTetragono(this.tetragonoGet(5, col));
	}

	this.fasiSet('ΔΙΑΠΡΑΓΜΑΤΕΥΣΗ');
	return this;
}

// Η μέθοδος που ακολουθεί αρχικοποιεί το ταμπλό και εφαρμόζει όλες τις ενέργειες τής
// τελευταίας παρτίδας.

Tablo.prototype.replay = function() {
	var tablo = this, trapezi, partida;

	this.stisimo();
	delete this.epomenos;
	delete this.nikitis;

	trapezi = this.trapeziGet();
	if (!trapezi) return this;

	partida = trapezi.partidaGet();
	if (!partida) return this;

	this.fasiSet('ΣΤΗΣΙΜΟ');
	partida.energiaWalk(function(i, energia) {
		var idos = energia.idosGet(), process;

		if (!idos) globals.fatal('απροσδιόριστο είδος ενέργειας');

		process = 'processEnergia' + idos;
		if (tablo[process]) return tablo[process].call(tablo, energia);

		globals.fatal(idos + ': απρόβλεπτο είδος ενέργειας');
	});

	return this;
}

Tablo.prototype.pagosEvilCount = function(pektis) {
	var pagos = this.pagosGet(pektis);
	if (!pagos) return 0;
	return pagos.evilCount();
}

Tablo.prototype.pagosGoodCount = function(pektis) {
	var pagos = this.pagosGet(pektis);
	if (!pagos) return 0;
	return pagos.goodCount();
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Tetragono = function(props) {
	globals.initObject(this, props, {
		recursive: false,
	});
}

Tetragono.prototype.tabloGet = function() {
	return this.tablo;
}

Tetragono.prototype.tabloFetch = function() {
	var tablo = this.tabloGet();
	if (tablo) return tablo;
	globals.fatal('απροσδιόριστο ταμπλό τετραγώνου');
}

Tetragono.prototype.rowGet = function() {
	return this.row;
}

Tetragono.prototype.rowFetch = function() {
	var row = this.rowGet();
	if (row) return row;
	globals.fatal('απροσδιόριστη γραμμή τετραγώνου');
}

Tetragono.prototype.colGet = function() {
	return this.col;
}

Tetragono.prototype.colFetch = function() {
	var col = this.colGet();
	if (col) return col;
	globals.fatal('απροσδιόριστη στήλη τετραγώνου');
}

Tetragono.prototype.pioniGet = function() {
	return this.pioni;
}

Tetragono.prototype.pioniFetch = function() {
	var pioni = this.pioniGet();
	if (pioni) return pioni;
	globals.fatal('απροσδιόριστο πιόνι τετραγώνου');
}

Tetragono.prototype.rcGet = function() {
	return geister.rc(this.rowFetch(), this.colFetch());
}

Tetragono.prototype.pioniClear = function() {
	delete this.pioni;
	return this;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Pagos = function(props) {
	this.pioni = [];
	globals.initObject(this, props);
}

Pagos.prototype.tabloGet = function() {
	return this.tablo;
}

Pagos.prototype.evilCount = function() {
	var i, count = 0;
	for (i in this.pioni) {
		if (this.pioni[i].isEvil()) count++;
	}
	return count;
}

Pagos.prototype.goodCount = function() {
	var i, count = 0;
	for (i in this.pioni) {
		if (this.pioni[i].isGood()) count++;
	}
	return count;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////@

Pioni = function(props) {
	globals.initObject(this, props, {
		recursive: false,
	});
}

Pioni.prototype.tabloGet = function() {
	return this.tablo;
}

Pioni.prototype.pektisGet = function() {
	return this.pektis;
}

Pioni.prototype.pektisFetch = function() {
	var pektis = this.pektisGet();
	if (pektis) return pektis;
	globals.fatal('απροσδιόριστος παίκτης πιονιού');
}

Pioni.prototype.noumeroGet = function() {
	return this.noumero;
}

Pioni.prototype.noumeroFetch = function() {
	var noumero = this.noumeroGet();
	if (noumero) return noumero;
	globals.fatal('απροσδιόριστο νούμερο πιονιού');
}

Pioni.prototype.pagosGet = function() {
	return this.pagos;
}

Pioni.prototype.tetragonoGet = function() {
	return this.tetragono;
}

Pioni.prototype.isPagos = function() {
	return this.pagosGet();
}

Pioni.prototype.oxiPagos = function() {
	return !this.isPagos();
}

Pioni.prototype.isGood = function() {
	return(this.noumeroFetch() < 5);
}

Pioni.prototype.oxiGood = function() {
	return !this.isGood();
}

Pioni.prototype.isEvil = function() {
	return(this.noumeroFetch() > 4);
}

Pioni.prototype.valePago = function() {
	var tablo = this.tabloGet(), pektis = this.pektisGet();

	if (this.tetragono) {
		delete this.tetragono.pioni;
		delete this.tetragono;
	}

	this.pagos = tablo.pagosGet(pektis).pioni.push(this);
	return this;
}

Pioni.prototype.valeTetragono = function(tetragono) {
	var tablo = this.tabloGet(), pektis = this.pektisGet(), pagos;

	if (this.pagos) {
		pagos = tablo.pagosGet(pektis);
		pagos.pioni.splice(this.pagos, 1);
		delete this.pagos;
	}

	this.tetragono = tetragono;
	tetragono.pioni = this;
	return this;
}
